package model;

import java.awt.geom.Point2D;

import utils.MathUtils;
import utils.Point2D_DataPackage;
import ctf.model.Side;
import ctf.view.GameViewInterface;

import model.strategies.AbstractStrategy;
import model.strategies.EscapeeStrategy;
import model.strategies.FlagCarrierStrategy;
import model.strategies.MoveablePlayerStrategy;
import model.strategies.TCTFStrategy;
import model.strategies.UnmoveableEntityStrategy;

/**
 * A class to represent players on the capture the flag field.
 * @author N. Bhatia, A. Butt, N. Evans, C. Levesque, P. OKeefe
 *
 */
public class Player extends GameEntity implements IPublishable {
	private PlayerState state;
	private AbstractStrategy movementStrategy;
	private boolean playerIsVisible;

	public Player() {
		super(true);
		this.state = PlayerState.DEFENDING;
		this.movementStrategy = new MoveablePlayerStrategy(this);
	}

	/**
	 * Enumeration of available Player States
	 */
	public enum PlayerState {
		INVISIBLE(0), DEFENDING(1), ATTACKING(2), PRISONER(3), ESCAPEE(4), FLAG_CARRIER(
				5), TCTF(6);

		private int value;

		PlayerState(int value) {
			this.value = value;
		}

		public int value() {
			return this.value;
		}
	}
	
	/**
	 * Set the state of a player
	 * @param state - The new state to set
	 */
	public void setState(PlayerState state) {
		if (this.state != state) {
			this.state = state;
			if (state == PlayerState.ATTACKING
					||  state == PlayerState.DEFENDING) {
				this.movementStrategy = new MoveablePlayerStrategy(this);
			} else if (state == PlayerState.FLAG_CARRIER) {
				this.movementStrategy = new FlagCarrierStrategy(this);
			} else if (state == Player.PlayerState.PRISONER){
				this.movementStrategy = new UnmoveableEntityStrategy(this);
			} else if (state == Player.PlayerState.TCTF) {
				this.movementStrategy = new TCTFStrategy(this);
			} else if (state == Player.PlayerState.ESCAPEE) {
				this.movementStrategy = new EscapeeStrategy(this);
			}
		}
	}

	/**
	 * Get the current state of a player
	 * @return
	 */
	public PlayerState getState() {
		return this.state;
	}

	/**
	 * Returns information used for the view 
	 * @return
	 */
	public GameViewInterface.PlayerInfo getInfo() {
		return new PlayerInfo("", getOrientation(), getPosition());			
	}

	@Override
	public void update(int elapsedTimeInMillis) {
		this.movementStrategy.update(elapsedTimeInMillis);
	}

	@Override
	public String publish(Side side){
		String playerString = new String();
		playerString = " (";
		Point2D_DataPackage position = new Point2D_DataPackage();
		if(playerIsVisible || side == this.getSide()) {
			playerString += this.state.value() + " ";
			position.point = this.getPosition();
		}
		else {
			playerString += PlayerState.INVISIBLE.value() + " ";
			position.point = new Point2D.Double(0.0, 0.0);
		}
		playerString += position.encode() + " ";
		playerString += MathUtils.capValue((float)this.getSpeed()) + " ";
		playerString += MathUtils.capValue((float)this.getOrientation()) + ")";
		return playerString;
	}
	
	/**
	 * See the flags visibility position, pass in true to set the visible position
	 * to actual position, pass in false and visible position will be set to 0,0
	 * @param visible 
	 */
	public void setVisibility(boolean visible){
		playerIsVisible = visible;
	}

	/**
	 * Determines if the player is set to visible.
	 * @return players visiblity status.
	 */
	public boolean isPlayerVisible() {
		return playerIsVisible;
	}

	/**
	 * Determines if the player is on the defending side.
	 * @return True if player is on his own side.
	 */
	public boolean isOnDefendingSide() {
		if (this.getSide() == Side.LEFT) {
			if (this.getPosition().getX() <= GameConstants.FIELD_WIDTH/2 + GameConstants.PLAYER_RADIUS) {
				return true;
			} else {
				return false;
			}
		}
		else { // player on side right
			if (this.getPosition().getX() >= GameConstants.FIELD_WIDTH/2 - GameConstants.PLAYER_RADIUS) {
				return true;
			} else {
				return false;
			}
		}
	}
	
	/**
	 * Determines if a player is on the attacking side.
	 * @return True if a player is on the other teams side.
	 */
	public boolean isOnOffendingSide() {
		if (this.getSide() == Side.LEFT) {
			if (this.getPosition().getX() > GameConstants.FIELD_WIDTH/2 + GameConstants.PLAYER_RADIUS) {
				return true;
			} else {
				return false;
			}
		}
		else { // player on side right
			if (this.getPosition().getX() < GameConstants.FIELD_WIDTH/2 - GameConstants.PLAYER_RADIUS) {
				return true;
			} else {
				return false;
			}
		}
	}

	@Override
	public double getRadius() {
		return GameConstants.PLAYER_RADIUS;
	}

}
